#include <iostream>
#include <string>
#include <vector>
#include <cstdio>
#include <unistd.h>

#include "LockGuard.hpp"

using namespace std;

#define NUM 4
int tickets = 3000;

// class threadData
// {
// public:
//     threadData(int num, pthread_mutex_t *mutex)
//     {
//         threadname = "thread-" + to_string(num);
//         lock = mutex;
//     }

//     static void *GetTicket(void *args)
//     {
//         threadData *td = static_cast<threadData *>(args);
//         const char *name = td->threadname.c_str();
//         while (true)
//         {
//             //申请锁成功才往下执行，不成功，就阻塞等待
//             pthread_mutex_lock(td->lock);
//             pthread_mutex_lock(td->lock);
//             if (tickets > 0)
//             {
//                 // 若usleep在这里设置，此时在锁内，导致一个线程拿到锁后，休眠时仍持有锁，让其他线程无法竞争
//                 //  usleep(1000);
//                 printf("who:%s get a ticket:%d\n", name, tickets);
//                 tickets--;
//                 pthread_mutex_lock(td->lock);

//                 pthread_mutex_unlock(td->lock);
//                 // 放到这里后，在锁外，即让线程释放锁后休眠，让其他线程抢票机会
//                 usleep(1000);
//             }
//             else
//             {
//                 pthread_mutex_unlock(td->lock);
//                 pthread_mutex_lock(td->lock);

//                 break;
//             }
//         }
//         usleep(13);
//         printf("%s....quit\n", name);
//         return nullptr;
//     }

// public:
//     string threadname;
//     static pthread_mutex_t *lock;
// };

// // 把锁简单封装
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;
class threadData
{
public:
    threadData(int num/*, pthread_mutex_t *mutex*/)
    {
        threadname = "thread-" + to_string(num);
        // lock = mutex;
    }

    static void *GetTicket(void *args)
    {
        threadData *td = static_cast<threadData *>(args);
        const char *name = td->threadname.c_str();
        while (true)
        {
            {
                LockGuard lockguard(&lock);
                if (tickets > 0)
                {
                    // usleep(100);
                    printf("who:%s get a ticket:%d\n", name, tickets);
                    tickets--;
                }
                else
                {

                    break;
                }
                
            }
            usleep(1000);
        }
        usleep(13);
        printf("%s....quit\n", name);
        return nullptr;
    }

public:
    string threadname;
    // static pthread_mutex_t *lock;
};

// 未封装
//  class threadData
//  {
//  public:
//      threadData(int num,pthread_mutex_t*mutex)
//      {
//          threadname="thread-"+to_string(num);
//          lock=mutex;
//      }

//     static void* GetTicket(void*args)
//     {
//         threadData*td=static_cast<threadData*>(args);
//         const char* name=td->threadname.c_str();
//         while(true)
//         {
//             pthread_mutex_lock(td->lock);
//             if(tickets>0)
//             {
//                 //若usleep在这里设置，此时在锁内，导致一个线程拿到锁后，休眠时仍持有锁，让其他线程无法竞争
//                 // usleep(1000);
//                 printf("who:%s get a ticket:%d\n",name,tickets);
//                 tickets--;
//                 pthread_mutex_unlock(td->lock);
//                 //放到这里后，在锁外，即让线程释放锁后休眠，让其他线程抢票机会
//                 usleep(1000);
//             }
//             else
//             {
//                 pthread_mutex_unlock(td->lock);
//                 break;
//             }
//         }
//         usleep(13);
//         printf("%s....quit\n",name);
//         return nullptr;
//     }

// public:
//     string threadname;
//     static pthread_mutex_t *lock;
// };
// pthread_mutex_t *threadData::lock = nullptr;

int main()
{
    // threadData::lock = new pthread_mutex_t;
    // pthread_mutex_init(threadData::lock, nullptr);
    pthread_mutex_init(&lock, nullptr);
    
    vector<pthread_t> tids;
    vector<threadData *> datas;
    for (int i = 1; i <= NUM; i++)
    {
        pthread_t tid;
        // threadData *td = new threadData(i, threadData::lock);
        threadData *td = new threadData(i);

        datas.push_back(td);
        pthread_create(&tid, nullptr, threadData::GetTicket, datas[i - 1]);
        tids.push_back(tid);
    }

    for (auto &tid : tids)
    {
        pthread_join(tid, nullptr);
    }
    for (auto &d : datas)
    {
        delete (d);
    }

    // pthread_mutex_destroy(threadData::lock);
        pthread_mutex_destroy(&lock);

    // delete threadData::lock;
    // delete lock;

    return 0;
}